package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"time"

	iplocation "gitee.com/thubcc/blockchain/iplocationservice"
	"gitee.com/thubcc/blockchain/p2p"
	ws "gitee.com/thubcc/blockchain/websocket"
	"github.com/ethereum/go-ethereum/log"
	"github.com/ethereum/go-ethereum/p2p/enode"
	"github.com/ethereum/go-ethereum/params"
	"github.com/gorilla/websocket"
)

var (
	toUI   = make(chan Res,5)
	fromUI = make(chan string)
	db = &iplocation.IPDB{DBFile:".resources/GeoLite2-City.mmdb"}
	logRec = make(map[string]string)
)
type Res struct{
    Longitude float64
    Latitude  float64
    IPV4      string
}
type record struct {
	Addr string `json:"addr"`
	ID   string `json:"id"`
}
func SetupWs() {
	w,err := ws.NewWsServer("ws://127.0.0.1:9001/msg")
	if err != nil {
		fmt.Println("Error setup Ws",err)
	}

    send := func(conn *websocket.Conn,stopCh chan int) {
		for{
			select {
			case <-stopCh:
				fmt.Println("connect closed")
				return
			case ipv4 := <-toUI:
				data,_ := json.Marshal(ipv4);
				err := conn.WriteMessage(1, []byte(data))
				if err != nil {
					fmt.Println("send msg faild ", err)
            		return
				}
				<-time.After(time.Microsecond * 100)
			}
		}
	}
	wsHandle := func(conn *websocket.Conn) {
		stopCh := make(chan int)
		defer func() {
			close(stopCh)
			conn.Close()
		}()
		go send(conn,stopCh)
    	for {
        	conn.SetReadDeadline(time.Now().Add(time.Millisecond * time.Duration(60000)))
       		_, msg, err := conn.ReadMessage()
        	if err != nil {
            	close(stopCh)
            	if netErr, ok := err.(net.Error); ok {
                	if netErr.Timeout() {
                    	fmt.Printf("ReadMessage timeout remote: %v\n", conn.RemoteAddr())
						return
					}
				}
				if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseNormalClosure) {
					fmt.Printf("ReadMessage other remote:%v error: %v \n", conn.RemoteAddr(), err)
				}
				return
        	}
			fmt.Println("client:", string(msg))
    	}
	}
	w.RegistHandle(wsHandle)
	go w.Start()
}

func processIp(i int,p *record) {
	// if addr,ok := logRec[p.ID]; ok && addr!=""{
	// 	return
	// }
	fmt.Printf("%5d %s %s\n",i,p.ID,p.Addr)
	if p.ID == "" || p.Addr == "" {
		return
	}
	// logRec[p.ID] = p.Addr
	Longitude,Latitude,err := db.Location(p.Addr)
	if err != nil {
		fmt.Printf("read addr %s failure: %v\n",p.Addr,err)
		return
	} 
	var ipr = Res{
		Longitude,
		Latitude,
		p.Addr,
	}
	select{
	case toUI<-ipr:
	default:
		return
	}
}

func LogProcessor(r *io.PipeReader,restart chan struct{}) {
	defer close(restart)
	err := db.Init()
	if err != nil {
		fmt.Printf("open db failure: %v\n",err)
	}
	defer db.Close()
	reader := bufio.NewReader(r)
	for i:=0;;i++ {
		line,err := reader.ReadString('\n')
		if err != nil {
			fmt.Println(err)
			return
		}
		var p = record{}
		err = json.Unmarshal([]byte(line), &p)
		if err == nil {
			processIp(i,&p)
		}
	}
}

func main() {
	SetupWs()
	r,w := io.Pipe()
	logHandler := log.StreamHandler(w, log.JSONFormat())
	log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(6),logHandler))

	var peers = []*enode.Node{}
	for _,mainnode := range params.MainnetBootnodes {
		peer := enode.MustParse(mainnode)
		peers = append(peers,peer)
	}
	
	server,err := p2p.NewNullNode(peers,nil)
	if err != nil {
		fmt.Println("init p2p failure",err)
	} else {
		fmt.Println("Setup p2p Server")
	}
	restart := make(chan struct{})
	go LogProcessor(r,restart)
	server.Start()
	defer server.Stop()
	<-restart
}
